package com.ndp.fb.walle.filters;

import com.alibaba.fastjson.JSON;
import com.ndp.fb.config.ConfigCentre;
import com.ndp.fb.constant.CommonConstants;
import com.ndp.fb.constant.ConfigConst;
import com.ndp.fb.enums.ErrorType;
import com.ndp.fb.rdb.model.User;
import com.ndp.fb.redis.api.RedisClient;
import com.ndp.fb.redis.model.UserProfile;
import com.ndp.fb.walle.model.vo.RespModel;
import com.ndp.fb.walle.util.ResponseUtils;
import org.apache.shiro.util.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * Created by Jacky on 2014/10/20.
 */
public class TicketValidateClientFilter implements Filter {
    private final org.slf4j.Logger log = LoggerFactory.getLogger(getClass());
    @Autowired
    private RedisClient redisClient;
    //万能钥匙，用的时候从配置中心获取
    private String masterKey;
    //是否安全单点登录
    private String isSecureSSO = ConfigCentre.getString(CommonConstants.IS_SECURE_SSO)!=null?ConfigCentre.getString(CommonConstants.IS_SECURE_SSO):"false";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        if (httpServletRequest.getRequestURL().toString().contains("/login")
                || httpServletRequest.getRequestURL().toString().contains("/sysmonitor")
                ||  httpServletRequest.getRequestURL().toString().contains("/websocket")
                ) {
            chain.doFilter(request, response);
            //白名单直接放过
            return;
        }
        if (redisClient == null) {
            log.error("redisClient is null.");
            doFail(httpServletResponse, ErrorType.INVALID_TOKEN);
            return;
        }
        //获取token
        String token = getCookieByName(httpServletRequest, "token");
        if (!StringUtils.hasText(token)) {
            log.info("token invalid.");
            doFail(httpServletResponse, ErrorType.INVALID_TOKEN);
            return;
        }
        // 验证token
        UserProfile p = (UserProfile)redisClient.get(token);
        if (p == null || p.getToken() == null || p.getUser() == null) {
            log.info("find session by token failed.");
            doFail(httpServletResponse, ErrorType.INVALID_TOKEN);
            return;
        } else {
            //杜绝使用user ticket 作为token 登录。
            if (token.equals(p.getUser().getTicket())) {
                doFail(httpServletResponse, ErrorType.INVALID_TOKEN);
            } else {
                doSuccess(httpServletRequest, httpServletResponse, chain, p);
            }
        }
    }


    private void doSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, UserProfile profile) throws IOException, ServletException {
        //Session用户
        User sessionUser = profile.getUser();
        UserProfile commonProfile = null;
        if (StringUtils.hasText(profile.getMasterKey())) {
            masterKey = ConfigCentre.getString(ConfigConst.MASTERKEY);
            if (profile.getMasterKey().equals(masterKey)) {
                commonProfile = (UserProfile) redisClient.get(masterKey);
            } else {
                //万能钥匙已更新，需要重新登录。 error 比较含蓄。
                doFail(response, ErrorType.ACCOUNT_CHANGED_EXCEPTION);
            }
        } else {
            commonProfile = (UserProfile) redisClient.get(sessionUser.getTicket());
        }
        if (commonProfile == null || commonProfile.getToken() == null) {
            doFail(response, ErrorType.INVALID_TOKEN);
            return;
        }
        //对比session用户和公共用户token，保障单点登录。
        if (isSecureSSO !=null && isSecureSSO.equals("true") && !commonProfile.getToken().equals(profile.getToken())) {
            //token不一致，帐号在其他地方登录，踢出当前用户。
            doFail(response, ErrorType.ACCOUNT_KICKOFF_EXCEPTION);
            redisClient.expire(profile.getToken(), 0, TimeUnit.MILLISECONDS.name());
            return;
        }
        request.getSession().setAttribute("user", sessionUser);
        //刷新过期时间
        redisClient.expire(profile.getToken(), 3, TimeUnit.DAYS.name());
        chain.doFilter(request, response);
    }

    private void doFail(HttpServletResponse response, ErrorType errorType) throws ServletException, IOException {
        RespModel result = new RespModel();
        result.setFlag("fail");
        result.setMsg(ResponseUtils.getMsg(errorType));
        result.setErrorCode(errorType.getCode().toString());
        response.getWriter().print(JSON.toJSONString(result));
    }

    /**
     * 获取request属性
     *
     * @param request
     * @param name
     * @return
     */
    private String getCookieByName(HttpServletRequest request, String name) {
        if (request == null) return null;
        Cookie[] cookies = request.getCookies();
        if (cookies == null || cookies.length < 1) {
            return null;
        }
        for (Cookie cookie : request.getCookies()) {
            if (cookie.getName().equals(name)) {
                return cookie.getValue();
            }
        }
        return null;
    }

    @Override
    public void destroy() {

    }
}
